﻿using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Profiling;

namespace Gameplay.PVE.Survivor
{
    public class BattleHelper
    {
        public static bool GetNavmeshPathPoint(NavMeshPath path,float moveSpeed,ref Vector3 originPosition)
        {
            if (path.corners.Length > 1)
            {
                var position = originPosition;
                var pathIndex = 0;
                while (moveSpeed > 0 && pathIndex < path.corners.Length)
                {
                    var direction = path.corners[pathIndex] - position;
                    if (direction.magnitude < moveSpeed)
                    {
                        position = path.corners[pathIndex];
                        moveSpeed -= direction.magnitude;
                        pathIndex++;
                    }
                    else
                    {
                        position += direction.normalized * moveSpeed;
                        moveSpeed = 0;
                    }
                }
                originPosition = position;
                return true;
            }
            else if (path.corners.Length == 1)
            {
                return false;
            }
            else
            {
                return false;
            }
        }

        public static bool IsInRegion(Vector3 position, BattleMapRegion region)
        {
            return IsInRegion(position, (EBattleRegionShapeType)region.shape, region.position, region.forward, region.shapeParam1,
                region.shapeParam2);
        }

        public static bool IsInRegion(Vector3 position,EBattleRegionShapeType shape,Vector3 center,Vector3 forward,float param1,float param2)
        {
            switch (shape)
            {
                case EBattleRegionShapeType.Circle:
                    var radius = param1;
                    return (center - position).sqrMagnitude <= radius * radius;
                case EBattleRegionShapeType.Rect:
                    var width = param1;
                    var height = param2;
                    var direction = position - center;
                    var right = Vector3.Cross(Vector3.up, forward);
                    var rightDot = Vector3.Dot(direction, right);
                    var forwardDot = Vector3.Dot(direction, forward);
                    return Mathf.Abs(rightDot) <= width / 2 && Mathf.Abs(forwardDot) <= height / 2;
                default:
                    return false;
            }
        }
        

        private static NavMeshPath path;
        
        public static Vector3 GetRandomPoint(float radius = 5,bool mustFarther = false)
        {
            if (path == null)
            {
                path = new NavMeshPath();
            }
            //角色周围随机一个点
            var teamGroupPosition = StageManager.Instance.TeamGroup.Position;
            var angle = UnityEngine.Random.Range(0, 360);
            var position = teamGroupPosition + new Vector3(Mathf.Cos(angle) * radius, 0, Mathf.Sin(angle) * radius);
            //首先先判断是否跟主角联通
            if (NavMesh.CalculatePath(position, teamGroupPosition, NavMesh.AllAreas, path) && path.status == NavMeshPathStatus.PathComplete)
            {
                position = GetNavmeshValidPosition(position);
            }
            else
            {
                //BattleDebug.LogError("点不合法 " + position);
                if (NavMesh.Raycast(teamGroupPosition, position, out var hit, NavMesh.AllAreas))
                {
                    position = hit.position;
                }
                else
                {
                    if (BattleDebug.enableLog)
                    {
                        BattleDebug.LogError("找不到点 " + position);
                    }
                }
            }

            if (!mustFarther)
            {
                return position;
            }
            //判断位置是否过近了
            var distance = Vector3.Distance(position, teamGroupPosition);
            if (distance < (radius - 2))
            {
                if (BattleDebug.enableLog)
                {
                    BattleDebug.LogError("寻找出生点 " + position);
                }
                position = StageManager.Instance.GetNearestSpawnPoint(radius, teamGroupPosition);
                if (position == BattleConstValue.DEFAULT_POINT)
                {
                    if (BattleDebug.enableLog)
                    {
                        BattleDebug.LogError("找不到出生点");
                    }
                }
                else
                {
                    if (BattleDebug.enableLog)
                    {
                        BattleDebug.LogError("找到了出生点 " + position);
                    }
                }
            }
            return position;
        }

        public static Vector3 GetNavmeshValidPosition(Vector3 position,float firstDistance = 1f)
        {
            if (SurvivorManager.isProfiling)
            {
                Profiler.BeginSample("GetRandomPoint.NavMesh.SamplePosition");
            }
            if (!NavMesh.SamplePosition(position, out var hit, firstDistance, NavMesh.AllAreas))
            {
                if (NavMesh.SamplePosition(position, out var hit2, 50, NavMesh.AllAreas))
                {
                    position = hit2.position;
                }
            }
            else
            {
                position = hit.position;
            }
            if (SurvivorManager.isProfiling)
            {
                Profiler.EndSample();
            }
            return position;
        }

        public static int GetTeamMask(int selfTeam, bool isOpposite = false)
        {
            var targetTeam = selfTeam;
            if (isOpposite)
            {
                targetTeam = 2 - targetTeam;
            }
            return 1 << targetTeam;
        }

        public static bool IsTeamMatch(int team, int teamMask)
        {
            return (teamMask & (1 << team)) > 0;
        }
        
        public static int GetTeamMaskByEnum(ETargetTeam team)
        {
            var mask = 0;
            if (team == ETargetTeam.Self)
            {
                mask = mask | 1;
            }
            else if (team == ETargetTeam.Enemy)
            {
                mask = mask | 4;
            }
            else if (team == ETargetTeam.All)
            {
                mask = mask | 1 | 4;
            }
            return mask;
        }

        public static bool GetRandomValue(float targetValue)
        {
            return UnityEngine.Random.Range(0, 1f) < targetValue;
        }

        public static void DrawCircle(Vector3 position, float radius, int samplePoint = 10)
        {
            var interval = 360 / samplePoint;
            var forward = Vector3.forward;
            for (int i = 0; i < samplePoint; i++)
            {
                var angle = i * interval;
                var dir = Quaternion.Euler(0, angle, 0) * forward;
                var dir2 = Quaternion.Euler(0, angle + interval, 0) * forward;
                var startPosition = position + new Vector3(0, 0.3f, 0);
                Gizmos.DrawLine(startPosition + dir * radius, startPosition + dir2 * radius);
            }
        }

        public static void DrawSector(Vector3 position, Vector3 forward, float angle, float radius,
            int samplePoint = 10)
        {
            var interval = angle / samplePoint;
            var halfAngle = angle / 2;
            var leftForward = Quaternion.AngleAxis(-halfAngle, Vector3.up) * forward;
            var rightForward = Quaternion.AngleAxis(halfAngle, Vector3.up) * forward;
            Gizmos.DrawLine(position,position + leftForward * radius);
            for (int i = 0; i < samplePoint; i++)
            {
                var a = i * interval;
                var dir = Quaternion.Euler(0, a, 0) * leftForward;
                var dir2 = Quaternion.Euler(0, a + interval, 0) * leftForward;
                var startPosition = position + new Vector3(0, 0f, 0);
                Gizmos.DrawLine(startPosition + dir * radius, startPosition + dir2 * radius);
            }
            Gizmos.DrawLine(position + rightForward * radius,position);
        }

        public static void DrawRect(Vector3 position, Vector3 forward, float width, float height)
        {
            forward = forward.normalized;
            position = position + forward * height / 2;
            var leftForward = Quaternion.AngleAxis(-90, Vector3.up) * forward;
            var p1 = position + forward * height / 2 + leftForward * width / 2;
            var p2 = position + forward * height / 2 - leftForward * width / 2;
            var p3 = position - forward * height / 2 - leftForward * width / 2;
            var p4 = position - forward * height / 2 + leftForward * width / 2;
            Gizmos.DrawLine(p1, p2);
            Gizmos.DrawLine(p2, p3);
            Gizmos.DrawLine(p3, p4);
            Gizmos.DrawLine(p4, p1);
        }

        public static bool IsSurvivorTeamCollider(Collider collider)
        {
            if (StageManager.Instance.TeamGroup == null)
            {
                return false;
            }

            return collider == StageManager.Instance.TeamGroup.Collider;
        }

        public static bool IsBlockByWall(Vector3 startPosition, Vector3 endPosition)
        {
            if (Physics.Raycast(startPosition,endPosition - startPosition,out var hitInfo, (endPosition - startPosition).magnitude, LayerMask.GetMask("city")))
            {
                //BattleDebug.LogError(hitInfo.collider.name);
                return true;
            }

            return false;
        }

        private static int normalMask = NavMesh.GetAreaFromName("Walkable");
        private static int mainUnitObstacleMask = NavMesh.GetAreaFromName("MainUnitObstacle");
        private static int blockMask = NavMesh.GetAreaFromName("Not Walkable");
        
        public static int GetNavmeshMask(bool isMain)
        {
            if (isMain)
            {
                return normalMask | blockMask;
            }
            else
            {
                return normalMask | mainUnitObstacleMask | blockMask;
            }
        }

        public static string GetTimeString(float time)
        {
            int minute = (int)time / 60;
            int second = (int)time % 60;
            return string.Format("{0:D2}:{1:D2}", minute, second);
        }

        public static Vector3 GetWorldPositionByScreenPosition(Camera camera, Vector2 screenPosition, float worldY = 0)
        {
            var ray = camera.ScreenPointToRay(screenPosition);
            var origin = ray.origin;
            var direction = ray.direction;
            var p = (origin.y - worldY) / direction.y;
            return origin - direction * p;
        }
        
        public static float DistanceFromPoint2Line(Vector3 p, Vector3 p1, Vector3 p2)
        {
            //求两点距离
            Vector3 p2p1 = p2 - p1;
            Vector3 p2p = p2 - p;
            var minValue = 0f;
            //只求线段
            if (Vector3.Dot(p2p1, p2p) < 0)
            {
                minValue = p2p.magnitude;
            }
            else if (Vector3.Dot(p1 - p2, p1 - p) < 0)
            {
                minValue = (p1 - p).magnitude;
            }
            //求p2p1 · p2p
            float dotResult = Vector3.Dot(p2p1, p2p);
            float rad = Mathf.Acos(dotResult / (p2p1.magnitude * p2p.magnitude));
            //求p到p1p2距离
            float distance = Mathf.Sin(rad) * p2p.magnitude;
            return Mathf.Max(distance,minValue);
        }

        private static Dictionary<float, EBattleResId> sectorDirectorResIdMap = new Dictionary<float, EBattleResId>
        {
            [30] = EBattleResId.DirectorSector30,
            [45] = EBattleResId.DirectorSector45,
            [60] = EBattleResId.DirectorSector60,
            [90] = EBattleResId.DirectorSector90,
            [120] = EBattleResId.DirectorSector120,
        };
        
        private static Dictionary<float, EBattleResId> sectorDirectorStorageResIdMap = new Dictionary<float, EBattleResId>
        {
            [30] = EBattleResId.DirectorSector30Storage,
            [45] = EBattleResId.DirectorSector45Storage,
            [60] = EBattleResId.DirectorSector60Storage,
            [90] = EBattleResId.DirectorSector90Storage,
            [120] = EBattleResId.DirectorSector120Storage,
        };
        
        public static EBattleResId GetSectorDirectorResId(float angle, bool isStorage = false)
        {
            var nearestAngle = 30f;
            var targetMap = isStorage ? sectorDirectorStorageResIdMap : sectorDirectorResIdMap;
            foreach (var key in targetMap.Keys)
            {
                if (Mathf.Abs(angle - key) < Mathf.Abs(angle - nearestAngle))
                {
                    nearestAngle = key;
                }
            }
            return targetMap[nearestAngle];
        }


        public static Vector3 MoveByNavMesh(Vector3 pos, Vector3 targetPosition, float speed,float maxDistance = -1)
        {
            var frameLength = Time.deltaTime * speed;
            if (path == null)
            {
                path = new NavMeshPath();
            }
            if (!NavMesh.CalculatePath(pos, targetPosition, NavMesh.AllAreas, path))
            {
                if (NavMesh.SamplePosition(targetPosition, out var hit, 2, NavMesh.AllAreas))
                {
                    targetPosition = hit.position;
                    var result = NavMesh.CalculatePath(pos, targetPosition, NavMesh.AllAreas, path);
                    if (result)
                    {
                        BattleHelper.GetNavmeshPathPoint(path, frameLength,ref pos);
                    }
                    else
                    {
                        pos += (targetPosition - pos).normalized * frameLength;
                    }
                }
            }
            else
            {
                if (maxDistance > 0)
                {
                    //判断路径是否合法
                    var distance = 0f;
                    for (int i = 0; i < path.corners.Length - 1; i++)
                    {
                        distance += Vector3.Distance(path.corners[i], path.corners[i + 1]);
                        if (distance > maxDistance)
                        {
                            return BattleConstValue.DEFAULT_POINT;
                        }
                    }
                }
                //沿着路点走路
                BattleHelper.GetNavmeshPathPoint(path, frameLength,ref pos);
            }
            return pos;
        }

        public static bool IsNightScene(string name)
        {
            return name.Contains("Night");
        }
        
        public static bool UpdateValueChange(ref float currentValue,ref float targetValue,float speed,float deltaTime = 0)
        {
            if (deltaTime == 0)
            {
                deltaTime = Time.deltaTime;
            }
            if (currentValue != targetValue)
            {
                var diff = targetValue - currentValue;
                var frameDiff = speed * deltaTime;
                if (Mathf.Abs(diff) < frameDiff)
                {
                    currentValue = targetValue;
                }
                else
                {
                    currentValue += diff > 0 ? frameDiff : -frameDiff;
                }

                return true;
            }

            return false;
        }

        public static Vector2Int GetGridByPosition(Vector3 point, Vector3 center, Quaternion q,float gridSize)
        {
            var diff = point - center;
            var realDiff = q * diff;
            var x = Mathf.RoundToInt(realDiff.x / gridSize);
            var z = Mathf.RoundToInt(realDiff.z / gridSize);
            return new Vector2Int(x, z);
        }

        public static Vector3 GetGroundPointByScreenPoint(Camera camera, Vector2 screenPosition)
        {
            var ray = camera.ScreenPointToRay(screenPosition);
            Vector3 dir = ray.direction;
            var position = ray.origin;
            float height = position.y;
            Vector3 offset = dir * height / dir.y;
            return position - offset;
        }

        public static Vector3 GetBossDeathForward(Vector3 position, Vector3 forward)
        {
            var targetPosition = position - forward.normalized * 2;
            if (NavMesh.CalculatePath(position, targetPosition, NavMesh.AllAreas, path) && path.status == NavMeshPathStatus.PathComplete)
            {
                return forward;
            }

            if (NavMesh.SamplePosition(targetPosition, out var hit, 2, NavMesh.AllAreas))
            {
                return hit.position - position;
            }
            if (BattleDebug.enableLog)
            {
                BattleDebug.LogError("找不到可以死亡的点 " + position + " " + forward + " " + targetPosition);  
            }
            return forward;
        }

        /*public static Vector3 MoveByNavMesh(Vector3 position, Vector3 targetPosition, float speed)
        {
            if (path == null)
            {
                path = new NavMeshPath();
            }
            if (!NavMesh.CalculatePath(position, targetPosition, NavMesh.AllAreas, path))
            {
                if (NavMesh.SamplePosition(targetPosition, out var hit, 2, NavMesh.AllAreas))
                {
                    targetPosition = hit.position;
                    var result = NavMesh.CalculatePath(position, targetPosition, NavMesh.AllAreas, path);
                    if (result)
                    {
                        BattleHelper.GetNavmeshPathPoint(path, frameLength,ref position);
                    }
                }
            }
            else
            {
                //沿着路点走路
                BattleHelper.GetNavmeshPathPoint(path, frameLength,ref position);
            }
        }*/
    }
}
